#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/mod_devicetable.h>
#include <linux/log2.h>
#include <linux/bitops.h>
#include <linux/jiffies.h>
#include <linux/of.h>
#include <linux/fs.h>
#include <linux/acpi.h>
#include <linux/i2c.h>
#include <asm/uaccess.h>
#include <linux/err.h>
#include <linux/ioctl.h>

#define Write 0x11
#define Read  0x12

static int major;
struct i2c_client * mpu6050_client;
struct class *mpu6050_class;

static int mpu6050_i2c_wirte_byte(unsigned int arr_len, unsigned char * buf)
{
    int err = 0;
    struct i2c_msg mpu6050_msg[1];
    mpu6050_msg[0].addr     = mpu6050_client->addr;
    mpu6050_msg[0].flags    = 0;        //写方向
    mpu6050_msg[0].len      = arr_len;
    mpu6050_msg[0].buf      = buf;
    err = i2c_transfer(mpu6050_client->adapter, mpu6050_msg, 1);
    mdelay(10);
    if(err != 1) {
        printk("i2c_transfer err = %d addr = %x!\n", err, mpu6050_msg[0].addr);
        return -1;
    }
    return 0;
}

static int mpu6050_i2c_read_byte(unsigned char *reg, unsigned char* pbuf)
{
    int err = 0;

    struct i2c_msg mpu6050_msg[2] = {
        {
            .addr   = mpu6050_client->addr,
            .flags  = 0,
            .len    = 1,
            .buf    = reg,
        },
        {
            .addr   = mpu6050_client->addr,
            .flags  = I2C_M_RD,
            .len    = 1,
            .buf    = pbuf,            
        },
    };
    err = i2c_transfer(mpu6050_client->adapter, mpu6050_msg, 2);
    if(err != 2) {
        printk("i2c_transfer err = %d addr = %x!\n", err, mpu6050_msg[0].addr);
        return -1;
    }
    return 0;
}

/*  当ioctl函数的第三个参数直接传递值时，不需要使用copy_from_user，
    因为此时值已经在内核空间中，可以直接使用。但如果ioctl函数的第三
    个参数传递的是地址，即传递的是用户空间的地址，那么就需要使用
    copy_from_user来将用户空间的数据拷贝到内核空间。这是因为内核和
    用户空间是隔离的，直接访问用户空间的内存是不安全的。           */
static long mpu6050_ioctl(struct file * file, unsigned int cmd, unsigned long arg)
{
    int err = 0;
    unsigned char buf[2];
    unsigned char rbuf;
    unsigned char data = 0;
    unsigned char *test;

    if(_IOC_TYPE(cmd) == 'A') {
        cmd = 2;
    } else {
        cmd = 1;
    }

    switch (cmd)
    {
    case 1:
        err = copy_from_user(buf, (unsigned char *)arg, 2);
        mpu6050_i2c_wirte_byte(2, buf);
        break;
    case 2:
        err = copy_from_user(&rbuf, (unsigned char *)arg, 1);
        if(err) {
            printk("copy_from  , err: %d \n", err);
        }
        mpu6050_i2c_read_byte(&rbuf, &data);
        err = copy_to_user((unsigned char *)arg, &data, 1);
        test = (unsigned char *)arg;
        if(err) {
            printk("copy_to    , err: %d \n", err);
        }
        break;

    default :
        printk("***i2c cmd err,%s %s line: %d \n",__FILE__, __FUNCTION__, __LINE__);
        return -1;
    }
    return 0;
}


static struct file_operations mpu6050_fops = {
    .owner = THIS_MODULE,
    .unlocked_ioctl = mpu6050_ioctl,
};

static struct of_device_id mpu6050_match[] = {
    {.compatible = "PB, mpu6050"},
    {},
};

struct i2c_device_id mpu6050_id_table[] = {
    {"pb_mpu6050", (kernel_ulong_t)NULL},
    {},
};

static int mpu6050_probe(struct i2c_client *client, const struct i2c_device_id * id)
{
    int err = 0;
    struct device *pdev;

    mpu6050_client = client;    //传给全局
    major = register_chrdev(0, "mpu6050", &mpu6050_fops);
    if(major < 0) {
        printk("***ERR! ,%s %s line: %d \n",__FILE__, __FUNCTION__, __LINE__);
        err = major;
        goto major_err;
    }
    mpu6050_class =  class_create(THIS_MODULE, "mpu6050");
    if(IS_ERR(mpu6050_class)) {
        printk("***ERR! ,%s %s line: %d \n",__FILE__, __FUNCTION__, __LINE__);
        err = PTR_ERR(mpu6050_class);
        goto class_err;
    }
    pdev = device_create(mpu6050_class, NULL, MKDEV(major, 0), NULL, "pb_mpu6050");
    if(IS_ERR(pdev)) {
        printk("***ERR! ,%s %s line: %d \n",__FILE__, __FUNCTION__, __LINE__);
        err = PTR_ERR(pdev);
        goto dev_err;
    }
    printk("MPU6050 Driver is ready!\n");
    return err;

dev_err:
    class_destroy(mpu6050_class);
class_err:
    unregister_chrdev(major, "mpu6050");
major_err: 
    printk("MPU6050 Driver ERR! \n");
    return err;
}

static int mpu6050_remove(struct i2c_client *client) 
{
    device_destroy(mpu6050_class, MKDEV(major, 0));
    class_destroy(mpu6050_class);
    unregister_chrdev(major, "mpu6050");
    printk("MPU6050 Driver closed! \n");
    return 0;
}

static struct i2c_driver mpu6050_i2c_driver = {
    .driver = {
        .name = "pb_mpu6050",
        .of_match_table = mpu6050_match,
    },
    .probe = mpu6050_probe,
    .remove = mpu6050_remove,
    .id_table = mpu6050_id_table,
};
static int __init mpu6050_init(void)
{
    int ret;
    ret = i2c_add_driver(&mpu6050_i2c_driver);
    return 0;
}

static void __exit mpu6050_exit(void)
{
    i2c_del_driver(&mpu6050_i2c_driver);
}

module_init(mpu6050_init);
module_exit(mpu6050_exit);

MODULE_LICENSE("GPL");